#include "base64.h"

/**
 * @brief 格式化字符
 *
 * @param Character - 字符
 * @return int      - 转换后的实际数值
 */
static int Base64DecodeChar(char Character);

static const char Base64Table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

static int Base64DecodeChar(char Character)
{
  if ((Character >= 'A') && (Character <= 'Z'))
    return Character - 'A';

  if ((Character >= 'a') && (Character <= 'z'))
    return Character - 'a' + 26;

  if ((Character >= '0') && (Character <= '9'))
    return Character - '0' + 52;

  if (Character == '+')
    return 62;

  if (Character == '/')
    return 63;

  return -1;
}

bool Base64Encode(const uint8_t *pDataIn, uint32_t SizeIn, char *pDataOut)
{
  if ((pDataIn != NULL) && (pDataOut != NULL))
  {
    if (SizeIn == 0)
    {
      pDataOut[0] = '\0';
      return true;
    }

    {
      uint32_t OutputLength = 4 * ((SizeIn + 2) / 3);

      for (uint32_t i = 0, j = 0; i < SizeIn;)
      {
        uint32_t Octet_A = i < SizeIn ? pDataIn[i++] : 0;
        uint32_t Octet_B = i < SizeIn ? pDataIn[i++] : 0;
        uint32_t Octet_C = i < SizeIn ? pDataIn[i++] : 0;

        uint32_t Triple = (Octet_A << 0x10) + (Octet_B << 0x08) + Octet_C;

        pDataOut[j++] = Base64Table[(Triple >> 3 * 6) & 0x3F];
        pDataOut[j++] = Base64Table[(Triple >> 2 * 6) & 0x3F];
        pDataOut[j++] = Base64Table[(Triple >> 1 * 6) & 0x3F];
        pDataOut[j++] = Base64Table[(Triple >> 0 * 6) & 0x3F];
      }

      for (uint32_t i = 0; i < (3 - SizeIn % 3) % 3; i++)
      {
        pDataOut[OutputLength - 1 - i] = '=';
      }

      pDataOut[OutputLength] = '\0';

      return true;
    }
  }

  return false;
}

bool Base64Decode(const char *pDataIn, uint32_t SizeIn, uint8_t *pDataOut, uint32_t *pSizeOut)
{
  if ((pDataIn != NULL) && (pDataOut != NULL) && (pSizeOut != NULL))
  {
    if (((SizeIn % 4) == 0) && (SizeIn != 0))
    {
      *pSizeOut = SizeIn / 4 * 3;
      if (pDataIn[SizeIn - 1] == '=')
        (*pSizeOut)--;
      if (pDataIn[SizeIn - 2] == '=')
        (*pSizeOut)--;

      for (uint32_t i = 0, j = 0; i < SizeIn;)
      {
        uint32_t Sextet_A = pDataIn[i] == '=' ? 0 & i++ : Base64DecodeChar(pDataIn[i++]);
        uint32_t Sextet_B = pDataIn[i] == '=' ? 0 & i++ : Base64DecodeChar(pDataIn[i++]);
        uint32_t Sextet_C = pDataIn[i] == '=' ? 0 & i++ : Base64DecodeChar(pDataIn[i++]);
        uint32_t Sextet_D = pDataIn[i] == '=' ? 0 & i++ : Base64DecodeChar(pDataIn[i++]);
        if ((Sextet_A == -1) || (Sextet_B == -1) || (Sextet_C == -1) || (Sextet_D == -1))
          return false;

        uint32_t Triple = (Sextet_A << 3 * 6) + (Sextet_B << 2 * 6) + (Sextet_C << 1 * 6) + (Sextet_D << 0 * 6);

        if (j < *pSizeOut)
          pDataOut[j++] = (Triple >> 2 * 8) & 0xFF;
        if (j < *pSizeOut)
          pDataOut[j++] = (Triple >> 1 * 8) & 0xFF;
        if (j < *pSizeOut)
          pDataOut[j++] = (Triple >> 0 * 8) & 0xFF;
      }

      return true;
    }
  }

  return false;
}
